# modLogicSignals.py
# Ben Terris 12/2/07
# translation of Matrix.java from Steve Varga

from modCalculatable import Calculatable
from modCalculatablePolynomial import CalculatablePolynomial
from modCalculatableNumber import CalculatableNumber

class Matrix(Calculatable):

    def __init__(self, datalist=[]):
        self._rows = len(datalist)
        if self._rows>0:
            self._cols = len(datalist[0])
            
        self._matrix = []
        
        for entry in datalist:
            self._matrix.append(entry)


        
            

    def getRows(self):
        return self._rows

    def getColumns(self):
        return self._cols
    

    def getMatrix(self):
        return self._matrix

    def __add__(self, rhs):
        '''Matrix addition.

        >>> print mat1 + mat1
        [[2, 4, 6, 8], [10, 12, 14, 16]]
        '''
        if not isinstance(rhs, Matrix):
            return None
    
        mat = rhs.get()
        rhsMat = mat.getMatrix()
        if (( self.getRows() == mat.getRows() )
            and ( self.getColumns() == mat.getColumns() ) ):
            # this is valid
            answerList = []
            for i in range(0, self.getRows()):
                rowList = []
                for j in range(0, self.getColumns()):
                    rowList.append( self._matrix[i][j] + rhsMat[i][j] )
                answerList.append( rowList )
            
            return Matrix(answerList )
        
        
    
    def __div__(self, rhs):
        '''Matrix division.
        >>> print mat3 / mat1
        [[2, 2, 2, 2], [2, 2, 2, 2]]
        '''
        # can only divide by matrix of same size (each element will divide
        # by its corresponding element in second matrix) or by a polynomial

        if isinstance(rhs, Matrix):
            mat = rhs.get()
            rhsMat = mat.getMatrix()

            if (( self.getRows() == mat.getRows() )
                and ( self.getColumns() == mat.getColumns() ) ):
                answerList = []
                for i in range(0, self.getRows()):
                    rowList = []
                    for j in range(0, self.getColumns()):
                        rowList.append( self._matrix[i][j] / rhsMat[i][j] )
                    answerList.append( rowList )
            
                return Matrix(answerList )
        else:
                answerList = []
                for i in range(0, self.getRows()):
                    rowList = []
                    for j in range(0, self.getColumns()):
                        tempAns = ( self._matrix[i][j] / rhsMat[i][j] )
                        if tempAns == None:
                            return None
                        else:
                            rowList.append( tempAns )
                    answerList.append( rowList )
                return Matrix (answerList )
            
    
    def __mul__(self, rhs):
        '''Matrix multiplication.
        >>> print mat1 * mat2
        None
        >>> print mat5 * mat6
        [[19, 16, 13, 23], [5, 6, 7, 6]]
        '''
        if isinstance(rhs, Matrix):
            mat = rhs.get()
            rhsMat = mat.getMatrix()
            answerList=[]
            if ( (self.getColumns() == mat.getRows() and self.getRows() != 0 ) ):
                # valid multiplication
                for i in range(0, self.getRows()):
                    rowList=[]
                    for j in range(0, mat.getColumns()):
                        toAdd = []
                        for k in range(0, self.getColumns()):
                            
                            toAdd.append( self._matrix[i][k]*rhsMat[k][j] )
#                            print "[",i,"][",k,"]->[",k,"][",j,"]",self._matrix[i][k]*rhsMat[k][j]
                        tempAns = toAdd[0]

                        for l in range(1, len(toAdd)):
                            tempAns += toAdd[l]
                        rowList.append( tempAns )
                        #print rowList
                    answerList.append(rowList)
                return Matrix(answerList )
            else:
				return None
	elif isinstance(rhs, CalculatableNumber):
			answerList = []
			for i in range(0,self.getRows()):
				rowList=[]
				for j in range(0,self.getColumns()):
					rowList.append(self._matrix[i][j] * rhs.value);
				answerList.append(rowList)
			return Matrix ( answerList )
	else:
		return None

    def __neg__(self):
        '''Matrix negation
        >>> print -mat1
        [[-1, -2, -3, -4], [-5, -6, -7, -8]]
        
        '''
        negList = []
        for i in range(0, self._rows):
            tempList=[]
            for j in range(0, self._cols):
                tempList.append( -self._matrix[i][j] )
            negList.append( tempList )

        return Matrix(negList )
        

    def square(self):
        '''Square a matrix. Only valid on square matrices (row = col)
        >>> print mat1.square()
        None
        >>> print mat2.square()
        [[16, 21], [28, 37]]
        '''

        if self._rows == self._cols:
            return self * self
        else:
            return None
        
    def get(self):
        '''Return this Matrix object'''
        return self

    def __str__(self):
        '''Get a string representation of this Matrix.
        >>> print mat1
        [[1, 2, 3, 4], [5, 6, 7, 8]]
        '''
        return self._matrix.__str__()

if __name__ == '__main__':
    # run a battery of unit tests to ensure correct functionality
    mat1 = Matrix([[1,2,3,4],[5,6,7,8]])
    mat2 = Matrix([[2,3],[4,5]])
    mat3 = Matrix([[2,4,6,8],[10,12,14,16]])
    mat4 = Matrix([[1],[1],[1],[1]])
    mat5 = Matrix([[1,3,4],[2,0,1]])
    mat6 = Matrix([[1,2,3,1],[2,2,2,2],[3,2,1,4]])

        
    def unittests():
        '''
        '''

    from doctest import testmod
    testmod(verbose=True)
